package com.gitee.qdbp.base.shiro.filter;

import java.io.IOException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import com.gitee.qdbp.able.result.IResultMessage;
import com.gitee.qdbp.able.result.ResponseMessage;
import com.gitee.qdbp.able.result.ResultCode;
import com.gitee.qdbp.base.i18n.IResultCodeI18nResolver;

/**
 * 认证过滤器, 支持JSON格式<br>
 * 只允许已认证的用户访问, <br>
 * 未认证的用户, 如果是JSON格式, 返回"用户未登录"的提示, 否则跳转到登录页面
 *
 * <pre>
 * &lt;bean id="authenticationShiroFilter" class="{pkg}.JsonSupportAuthenticationFilter"&gt;
 *     &lt;property name="jsonHttpMessageConverter" ref="jsonHttpMessageConverter" /&gt;
 * &lt;/bean&gt;
 * </pre>
 *
 * @author zhaohuihua
 * @version 150918
 */
public class JsonSupportAuthenticationFilter extends AccessControlFilter {

    private static final Logger log = LoggerFactory.getLogger(JsonSupportAuthenticationFilter.class);

    private IResultMessage unauthorized = ResultCode.UNAUTHORIZED;

    private JsonHttpMessageConverter<ResponseMessage> converter;

    @Autowired(required = false)
    private IResultCodeI18nResolver resultCodeI18nResolver;

    /** whether to stay compatible with HTTP 1.0 clients. **/
    protected boolean redirectHttp10Compatible = true;

    /**
     * 判断是否允许访问, 登录页面允许访问, 其他页面不允许<br>
     * {@inheritDoc}
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
            throws Exception {
        if (isLoginRequest(request, response)) {
            return true;
        } else {
            Subject subject = getSubject(request, response);
            return subject.isAuthenticated();
        }
    }

    /**
     * 访问被拒绝的处理<br>
     * {@inheritDoc}
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        Assert.notNull(converter, "JsonHttpMessageConverter must not be null");

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        if (log.isTraceEnabled()) {
            String uri = req.getRequestURI();
            log.trace("Attempting to access a path which requires authentication. [{}]", uri);
        }
        if (converter.isJsonRequest(req)) {
            // 如果是JSON请求, 以JSON格式输出"用户未登录"
            ResponseMessage rm = new ResponseMessage(unauthorized);
            if (resultCodeI18nResolver != null) {
                // 根据当时语言替换返回码对应的错误提示
                String message = resultCodeI18nResolver.getResultCodeMessage(rm.getCode());
                if (message != null) {
                    rm.setMessage(message);
                }
            }
            converter.write(rm, resp);
        } else {
            // 跳转至登录页
            saveRequestAndRedirectToLogin(request, response);
        }
        return false;
    }

    @Override
    protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
        String loginUrl = getLoginUrl();
        WebUtils.issueRedirect(request, response, loginUrl, null, true, redirectHttp10Compatible);
    }

    /** whether to stay compatible with HTTP 1.0 clients. **/
    public boolean isRedirectHttp10Compatible() {
        return redirectHttp10Compatible;
    }

    /** whether to stay compatible with HTTP 1.0 clients. **/
    public void setRedirectHttp10Compatible(boolean redirectHttp10Compatible) {
        this.redirectHttp10Compatible = redirectHttp10Compatible;
    }

    /**
     * 设置JsonHttpMessageConverter
     *
     * @param converter JsonHttpMessageConverter
     */
    public void setJsonHttpMessageConverter(JsonHttpMessageConverter<ResponseMessage> converter) {
        this.converter = converter;
    }

    /** 结果返回码的国际化处理类 **/
    public void setResultCodeI18nResolver(IResultCodeI18nResolver resultCodeI18nResolver) {
        this.resultCodeI18nResolver = resultCodeI18nResolver;
    }

}
